package com.patternity;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.patternity.depend.DependencyModel;
import com.patternity.dotgraph.DotDiagramWeaver;
import com.patternity.model.Catalog;
import com.patternity.model.DesignDeclaration;
import com.patternity.model.Element;
import com.patternity.model.Model;
import com.patternity.model.ModelBuilder;
import com.patternity.model.MotifWeaver;
import com.patternity.model.Pattern;
import com.patternity.model.Relation;
import com.patternity.model.SimpleModel;

/**
 * Represents the context used when browsing external metamodels to create the
 * given model(s) for Patternity
 * 
 * @author cyrille martraire
 */
public class MetaModelScannerContext implements Model, DependencyModel {

	/**
	 * Represents
	 * 
	 * @author cyrille martraire
	 */
	public enum TypeKind {

		INTERFACE, ABSTRACT, CONCRETE, FINAL;

	}

	/**
	 * Stores every design declaration built from scanning the annotations
	 */
	private final Collection<DesignDeclaration> declarations = new ArrayList<DesignDeclaration>();

	/**
	 * Internal given model
	 */
	private final Map<String, Element> elements = new HashMap<String, Element>();

	/**
	 * Hierarchy tree
	 */
	private final Map<String, List<String>> subtypes = new HashMap<String, List<String>>();

	/**
	 * Dependencies by element name
	 */
	private final Map<String, List<String>> dependencies = new HashMap<String, List<String>>();

	/**
	 * The registry of every known design model
	 */
	private final Catalog registry = new Catalog().setup();

	public void addDeclaration(final DesignDeclaration dd) {
		declarations.add(dd);
	}

	public void addElement(final String qName, final TypeKind kind,
			final String docComment) {
		final Element element = new Element(qName);
		elements.put(element.getName(), element);
	}

	@Override
	public Element getElement(final String name) {
		return elements.get(name);
	}

	@Override
	public Iterable<Element> elements() {
		return Collections.unmodifiableCollection(elements.values());
	}

	@Override
	public Iterable<Relation> relations() {
		return Collections.emptyList();
	}

	@Override
	public List<Element> getSubTypes(final Element superElement) {
		final List<Element> subElements = new ArrayList<Element>();
		final List<String> list = subtypes.get(superElement.getName());
		if (list == null) {
			return Collections.emptyList();
		}
		for (final String subtype : list) {
			subElements.add(getElement(subtype));
		}
		return subElements;
	}

	public void addSuper(final String qName, final String superQName) {
		System.out.println(qName + " ---|> " + superQName);
		List<String> list = subtypes.get(superQName);
		if (list == null) {
			list = new ArrayList<String>();
			subtypes.put(superQName, list);
		}
		list.add(qName);
	}

	public void addDependency(final String qName, final String dependQName) {
		System.out.println(qName + " ----> " + dependQName);
		List<String> list = subtypes.get(dependQName);
		if (list == null) {
			list = new ArrayList<String>();
			subtypes.put(dependQName, list);
		}
		list.add(qName);
	}

	public void done() {
		printAllToConsole();

		render(declarations);
	}

	private void printAllToConsole() {
		for (final DesignDeclaration designDeclaration : declarations) {
			System.out.println(designDeclaration);
		}
	}

	// declarations-driven
	public void render(final Iterable<DesignDeclaration> declarations) {
		final MotifWeaver weaver = new MotifWeaver();
		final StringBuilder sb = new StringBuilder();
		ModelBuilder displayModel = new SimpleModel();
		for (final DesignDeclaration d : declarations) {
			final String name = d.getName();
			final String simpleName = simpleName(name);
			final Pattern pattern = registry.get(simpleName);

			// TODO partition by pattern or group of patterns
			if (pattern != null) {
				sb.append("_" + simpleName);
				displayModel = weaver.generate(this, pattern, d);
			}
		}
		DotDiagramWeaver.render(sb.toString(), displayModel);
	}

	public final static String simpleName(final String name) {
		final int index = name.lastIndexOf('.');
		return index == -1 ? name : name.substring(index + 1);
	}

	@Override
	public Set<String> dependenciesFor(final Element element) {
		return null;
	}

	@Override
	public String toString() {
		return "MetaModelScannerContext";
	}

}
